package com.itmake.sample.toolbox.gis;

/**
 * 地图工具类
 * from web
 *
 * @author Amjacks
 * @since 2021/05/17
 */
public class MapUtil {

    private double axis = 6378245.0;
    private double offset = 0.00669342162296594323;
    private double x_pi = Math.PI * 3000.0 / 180.0;

    /**
     * 计算
     *
     * @param lat1 lat1
     * @param lng1 lng1
     * @param lat2 lat2
     * @param lng2 lng2
     * @return long
     */
    public long calculate(Double lat1, Double lng1, Double lat2, Double lng2) {
        Double radLat1 = lat1 * Math.PI / 180.0;
        Double radLat2 = lat2 * Math.PI / 180.0;
        Double a = radLat1 - radLat2;
        Double b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
        Double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
        s = s * 6378.137;
        return Math.round(s * 10000) / 10;
    }

    /**
     * GCJ-02=>BD09 火星坐标系=>百度坐标系
     *
     * @param glat glat
     * @param glon glon
     * @return {@link double[]}
     */
    public double[] gcj2BD09(double glat, double glon) {
        double x = glon;
        double y = glat;
        double[] latlon = new double[2];
        double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
        latlon[0] = z * Math.sin(theta) + 0.006;
        latlon[1] = z * Math.cos(theta) + 0.0065;
        return latlon;
    }

    /**
     * WGS84=》GCJ02   地球坐标系=>火星坐标系
     * @param wgLat
     * @param wgLon
     * @return
     */
    public double[] wgs2GCJ(double wgLat, double wgLon) {
        double[] latlon = new double[2];
        if (outOfChina(wgLat, wgLon)) {
            latlon[0] = wgLat;
            latlon[1] = wgLon;
            return latlon;
        }
        double[] deltaD = delta(wgLat, wgLon);
        latlon[0] = wgLat + deltaD[0];
        latlon[1] = wgLon + deltaD[1];
        return latlon;
    }

    /**
     * WGS84=》BD09   地球坐标系=>百度坐标系
     * @param wgLat
     * @param wgLon
     * @return
     */
    public double[] wgs2BD09(double wgLat, double wgLon) {
        double[] latlon = wgs2GCJ(wgLat, wgLon);
        return gcj2BD09(latlon[0], latlon[1]);
    }

    /**
     * 转换地址
     *
     * @param jwd jwd
     * @return {@link String}
     */
    private String transformPos(String jwd) {
        if (jwd != null && !jwd.trim().equals("") && (jwd.contains("°"))) {//如果不为空并且存在度单位
            //计算前进行数据处理
            jwd = jwd.replace("E", "").replace("N", "").replace(":", "").replace("：", "");
            double d = 0, m = 0, s = 0;
            d = Double.parseDouble(jwd.split("°")[0]);
            //不同单位的分，可扩展
            if (jwd.contains("′")) {//正常的′
                m = Double.parseDouble(jwd.split("°")[1].split("′")[0]);
            } else if (jwd.contains("'")) {//特殊的'
                m = Double.parseDouble(jwd.split("°")[1].split("'")[0]);
            }
            //不同单位的秒，可扩展
            if (jwd.contains("″")) {//正常的″
                //有时候没有分 如：112°10.25″
                s = jwd.contains("′") ? Double.parseDouble(jwd.split("′")[1].split("″")[0]) : Double.parseDouble(jwd.split("°")[1].split("″")[0]);
            } else if (jwd.contains("''")) {//特殊的''
                //有时候没有分 如：112°10.25''
                s = jwd.contains("'") ? Double.parseDouble(jwd.split("'")[1].split("''")[0]) : Double.parseDouble(jwd.split("°")[1].split("''")[0]);
            }
            jwd = String.valueOf(d + m / 60 + s / 60 / 60);//计算并转换为string
        }
        return jwd;
    }


    /**
     * 转换经纬度
     *
     * @param lat 纬度
     * @param lng 液化天然气
     * @return {@link double[]}
     */
    private double[] transformLatAndLng(String lat,String lng) {
        String sLat = transformPos(lat);
        String sLng = transformPos(lng);
        double[] doubles = wgs2BD09(Double.valueOf(sLat), Double.valueOf(sLng));
        return doubles;
    }

    /**
     * 转换纬度
     *
     * @param x x
     * @param y y
     * @return double
     */
    private double transformLat(double x, double y) {
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    /**
     * 转换经度
     *
     * @param x x
     * @param y y
     * @return double
     */
    private double transformLon(double x, double y) {
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0;
        return ret;
    }

    /**
     * @param wgLat wg纬度
     * @param wgLon wg经度
     * @return {@link double[]}
     */
    private double[] delta(double wgLat, double wgLon) {
        double[] latlng = new double[2];
        double dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
        double dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
        double radLat = wgLat / 180.0 * Math.PI;
        double magic = Math.sin(radLat);
        magic = 1 - offset * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((axis * (1 - offset)) / (magic * sqrtMagic) * Math.PI);
        dLon = (dLon * 180.0) / (axis / sqrtMagic * Math.cos(radLat) * Math.PI);
        latlng[0] = dLat;
        latlng[1] = dLon;
        return latlng;
    }

    /**
     * 国界
     *
     * @param lat 纬度
     * @param lon 经度
     * @return boolean
     */
    private boolean outOfChina(double lat, double lon) {
        if (lon < 72.004 || lon > 137.8347)
            return true;
        if (lat < 0.8293 || lat > 55.8271)
            return true;
        return false;
    }
}
